﻿using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using TiffLibrary;

namespace WholeSlideImageLibrary.Formats.GenericTiff
{
    internal sealed class FileSourceReaderCacheContentSource : TiffFileContentSource
    {
        private readonly FileSourceReaderCache _cache;

        public FileSourceReaderCacheContentSource(FileSourceReaderCache cache)
        {
            _cache = cache;
        }

        public override TiffFileContentReader OpenReader()
        {
            throw new NotSupportedException();
        }

        public override async ValueTask<TiffFileContentReader> OpenReaderAsync(CancellationToken cancellationToken = default)
        {
            FileSourceReader? reader = await _cache.RentAsync(cancellationToken).ConfigureAwait(false);
            return new ContentReader(reader, _cache);
        }

        protected override void Dispose(bool disposing) { }

        internal sealed class ContentReader : TiffFileContentReader
        {
            private FileSourceReader? _reader;
            private readonly FileSourceReaderCache _cache;

            public ContentReader(FileSourceReader reader, FileSourceReaderCache cache)
            {
                _reader = reader;
                _cache = cache;
            }

            public override int Read(TiffStreamOffset offset, Memory<byte> buffer)
            {
                throw new NotSupportedException();
            }

            public override ValueTask<int> ReadAsync(TiffStreamOffset offset, ArraySegment<byte> buffer, CancellationToken cancellationToken = default)
            {
                FileSourceReader reader = EnsureNotDisposed();
                return reader.ReadAsync(offset.ToInt64(), buffer.AsMemory(), cancellationToken);
            }

            public override ValueTask<int> ReadAsync(TiffStreamOffset offset, Memory<byte> buffer, CancellationToken cancellationToken = default)
            {
                FileSourceReader reader = EnsureNotDisposed();
                return reader.ReadAsync(offset.ToInt64(), buffer, cancellationToken);
            }

            private FileSourceReader EnsureNotDisposed()
            {
                FileSourceReader? reader = _reader;
                if (reader is null)
                {
                    ThrowObjectDisposedException();
                }
                return reader;
            }

            [DoesNotReturn]
            private static void ThrowObjectDisposedException()
            {
                throw new ObjectDisposedException(nameof(ContentReader));
            }

            protected override void Dispose(bool disposing)
            {
                if (!(_reader is null))
                {
                    _ = _cache.ReturnAsync(_reader).AsTask();
                    _reader = null;
                }
            }

            public override async ValueTask DisposeAsync()
            {
                if (!(_reader is null))
                {
                    await _cache.ReturnAsync(_reader).ConfigureAwait(false);
                    _reader = null;
                }
                await base.DisposeAsync().ConfigureAwait(false);
            }
        }
    }
}
